/*  This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.
    For more details, see the GNU Lesser General Public License (www.fsf.org
    or the COPYING file somewhere in the package)
 */

#include "BundleInstallerService.hh"

/*! @file src/OSGi/BundleInstallerService.cc
    @brief Class OSGi::BundleInstallerService (non-inline) methods.
    @author @ref Guillaume_Terrissol
    @date 9th February 2009 - 22nd January 2014
    @note This file is distributed under the LGPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

#include "BundleMgr.hh"
#include "Exception.hh"

namespace OSGi
{
    /// @cond DEVELOPMENT

//------------------------------------------------------------------------------
//                Bundle Installer Service Private Implementation
//------------------------------------------------------------------------------

    /*! @brief BundleInstallerService private implementation.
        @version 0.8
        @internal
     */
    class BundleInstallerService::Private
    {
    public:

explicit            Private(Context::Ptr pContext);                         //!< Constructor.
        Bundle::Ptr installBundle(const std::string& pPath, int pId) const; //!< New bundle installation.

        std::weak_ptr<Context::Ptr::element_type>   mContext;               //!< Execution context.
        size_t                                      mCounter;               //!< Installed bundle counter.
    };


//------------------------------------------------------------------------------
//                                P-impl Methods
//------------------------------------------------------------------------------

    /*! @copydetails BundleInstallerService::BundleInstallerService(Context::Ptr)
     */
    BundleInstallerService::Private::Private(Context::Ptr pContext)
        : mContext{pContext}
        , mCounter{}
    { }


    /*! @param pPath Bundle file name, with either absolute or relative path
        @param pId   Id to assign to the bundle
        @return A new bundle instance, installed from @p pPath, with id @p pId
        @signal Context::bundleInstalled()
     */
    Bundle::Ptr BundleInstallerService::Private::installBundle(const std::string& pPath, int pId) const
    {
        auto    lBndl = mContext.lock()->bundleFactory()->makeBundle(pPath, pId, mContext.lock());

        if (lBndl.use_count() != 0)
        {
            mContext.lock()->registerInstalledBundle(lBndl);
            OSGI_EMIT mContext.lock()->bundleInstalled(lBndl);
        }

        return lBndl;
    }

    /// @endcond

//------------------------------------------------------------------------------
//              Bundle Installer Service : Constructor & Destructor
//------------------------------------------------------------------------------

    /*! @param pContext @ref OSGi_Context_Page
     */
    BundleInstallerService::BundleInstallerService(Context::Ptr pContext)
        : pthis{pContext}
    { }


    /*! Defaulted.
     */
    BundleInstallerService::~BundleInstallerService() = default;


//------------------------------------------------------------------------------
//              Bundle Installer Service : Interface
//------------------------------------------------------------------------------

    /*! @return The BundleInstallerService type name
     */
    std::string BundleInstallerService::staticName()
    {
        return typeid(BundleInstallerService).name();
    }


    /*! @param pPath Bundle file name, with either absolute or relative path
        @return A new bundle, installed from @p pPath
     */
    Bundle::Ptr BundleInstallerService::installBundle(const std::string& pPath) const
    {
        return pthis->installBundle(pPath, pthis->mCounter++);
    }


    /*! @param pSymbolicName Name of the bundle to replace
        @param pPath Filename, with path (either absolute or relative), of the
        replacement bundle
        @note The @p pSymbolicName bundle must be in the OSGi::kResolved or
        OSGi::kUninstalled state
        @throw A OSGi::BundleError instance if the new bundle couldn't be
        successfully installed
     */
    Bundle::Ptr BundleInstallerService::replaceBundle(const std::string& pSymbolicName, const std::string& pPath) const
    {
        auto    lToReplace = pthis->mContext.lock()->findBundle(pSymbolicName);
        if      (!lToReplace)
        {
            throw BundleError{"Can't replace missing bundle " + pSymbolicName};
        }
        else if (lToReplace->state() == kResolved)
        {
            lToReplace->act(kUninstall, pthis->mContext.lock());
        }

        if (lToReplace->state() != kUninstalled)
        {
            throw BundleError{"Bundle " + pSymbolicName + " couldn't be uninstalled"};
        }

        auto    lNew = pthis->installBundle(pPath, lToReplace->id());
        if (lNew.use_count() == 0)
        {
            throw BundleError{"Couldn't install " + pPath};
        }
        if (!lNew->act(kResolve, pthis->mContext.lock()))
        {
            throw BundleError{"Couldn't resolve " + pPath};
        }
        lToReplace->swap(lNew);

        return lToReplace;
    }


//------------------------------------------------------------------------------
//                   Bundle Installer Service : Implementation
//------------------------------------------------------------------------------

    /*! @copydetails Service::isA(const std::string& pTypeName) const
     */
    bool BundleInstallerService::isType(const std::string& pTypeName) const
    {
        return (staticName() == pTypeName) || Service::isType(pTypeName);
    }


    /*! @copydetails Service::typeName() const
     */
    std::string BundleInstallerService::typeName() const
    {
        return staticName();
    }
}
